package com.example.aop;

import com.alibaba.fastjson.JSONObject;
import com.example.mapper.OperateLogMapper;
import com.example.pojo.OperateLog;
import com.example.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;

/**
 * @author hyk~
 */
@Slf4j // 为当前类引入日志功能，方便打印日志信息
@Component // 将当前类作为一个组件管理起来，Spring会自动扫描并管理它
@Aspect // 声明该类为一个切面类，用于定义横切关注点，即对方法执行前后进行拦截和处理
public class LogAspect {

    @Autowired
    private HttpServletRequest request; // 自动注入HttpServletRequest对象，用于获取请求信息

    @Autowired
    private OperateLogMapper operateLogMapper; // 自动注入OperateLogMapper对象，用于操作数据库记录操作日志

    @Around("@annotation(com.example.anno.Log)") // 环绕通知：拦截被@Log注解标记的方法，执行前后插入额外逻辑
    public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取请求头中的JWT令牌并解析，提取操作人的ID
        String jwt = request.getHeader("token"); // 从请求头中获取JWT令牌
        Claims claims = JwtUtils.parseJWT(jwt); // 解析JWT令牌，获取其中的声明
        Integer operateUser = (Integer) claims.get("id"); // 从声明中提取操作人ID

        // 获取操作时间，记录当前时间
        LocalDateTime operateTime = LocalDateTime.now();

        // 获取操作的类名，通过连接点获取目标类的名称
        String className = joinPoint.getTarget().getClass().getName();

        // 获取操作的方法名，通过连接点获取目标方法的名称
        String methodName = joinPoint.getSignature().getName();

        // 获取操作方法的参数，通过连接点获取方法的参数，并转换为字符串
        Object[] args = joinPoint.getArgs();
        String methodParams = Arrays.toString(args);

        // 记录操作开始时间，用于计算操作耗时
        Long begin = System.currentTimeMillis();

        // 调用目标方法，执行被拦截的方法
        Object result = joinPoint.proceed();

        // 记录操作结束时间
        Long end = System.currentTimeMillis();

        // 获取操作方法的返回值，并转换为JSON字符串
        String returnValue = JSONObject.toJSONString(result);

        // 计算操作耗时
        Long costTime = end - begin;

        // 创建操作日志对象，包含操作人、操作时间、类名、方法名、参数、返回值和耗时等信息
        OperateLog operateLog = new OperateLog(
                null, // 日志的主键ID，通常由数据库自动生成
                operateUser, // 操作人ID
                operateTime, // 操作时间
                className, // 操作类名
                methodName, // 操作方法名
                methodParams, // 操作方法参数
                returnValue, // 操作方法返回值
                costTime // 操作耗时
        );

        // 将操作日志插入数据库，持久化存储
        operateLogMapper.insert(operateLog);

        // 打印操作日志，记录到日志文件中
        log.info("AOP操作日志：{}", operateLog);

        // 返回目标方法的执行结果
        return result;
    }
}
